package com.jade.framework.startup;

import java.io.File;
import java.io.FileWriter;
import java.net.URL;

import com.jade.framework.base.JadeSystem;
import com.jade.framework.base.text.VariableUtils;
import com.jade.framework.startup.def.ConfigDefinition;
import com.jade.framework.startup.def.HostDefinition;
import com.jade.framework.startup.def.ServerDefinition;
import com.jade.framework.startup.def.SystemDefinition;
import org.apache.commons.digester3.Digester;
import org.apache.commons.digester3.Rule;
import org.apache.commons.digester3.Substitutor;
import org.apache.commons.digester3.substitution.VariableExpander;
import org.apache.commons.digester3.substitution.VariableSubstitutor;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.xml.sax.Attributes;

/**
 * 服务器，代表一个虚拟机。
 *
 * @author sky
 * @version 1.0 14-8-19 上午10:23
 */
public class Server
{
    private Log log = JadeSystem.getLog();
    private String type;
    private String config;
    private boolean isStarted = false;
    private boolean direct = true;
    private FileSystemXmlApplicationContext applicationContext;

    public Server(String type, String config, boolean direct)
    {
        this.config = config;
        this.type = type;
        this.direct = direct;
    }

    public void start()
    {
        JadeSystem.setServerType(type);
        try {
            if (direct) {
                applicationContext = new FileSystemXmlApplicationContext(config);
                applicationContext.start();
                isStarted = true;
                applicationContext.registerShutdownHook();
            }
            else {
                log.info("Start server " + type + " ...");
                loadSystemMeta(config);
                ServerDefinition serverDefinition = SystemDefinition.getInstance().getServer(type);
                if (serverDefinition == null) {
                    throw new IllegalArgumentException("Can't get server definition!");
                }
                URL[] configList = serverDefinition.getConfigList();
                URL[] runList = serverDefinition.getRunList();
                String configFiles = StringUtils.join(toStringArray(configList, false), ',');
                String runFiles = StringUtils.join(toStringArray(runList, true), ',');
                log.info("Property files:" + configFiles);
                log.info("Context files:" + runFiles);
                JadeSystem.setProperty("jade.props", configFiles);
                applicationContext = new FileSystemXmlApplicationContext(toStringArray(runList, true));
                log.info("Start application context...");
                applicationContext.start();
                log.info("It's finished to start application context!");
                isStarted = true;
                applicationContext.registerShutdownHook();
                log.info("Start server " + type + " ... Done");
            }

            //写入当前线程的进程号到文件中
            writeServerPid(type);
        }
        catch (RuntimeException e) {
            log.error("Can't start server " + type, e);
            throw e;
        }
    }

    protected void writeServerPid(String type)
    {
        if (JadeSystem.isWindows()) {
            try {
                File pidFilepath = new File(JadeSystem.getHome() + File.separatorChar + "pid", type + ".pid");
                FileWriter writer = new FileWriter(pidFilepath);
                String processName = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
                String processID = processName.substring(0, processName.indexOf('@'));
                writer.write(processID);
                writer.close();
            }
            catch (Throwable t) {
                JadeSystem.getLog().warn("Can't wirte pid:", t);
            }
        }
    }

    public void stop()
    {
        if (applicationContext != null) {
            applicationContext.stop();
            applicationContext.close();
        }
        isStarted = false;
    }

    public boolean isStarted()
    {
        return isStarted;
    }

    protected String[] toStringArray(URL[] urls, boolean abs)
    {
        String[] array = new String[urls.length];
        for (int i = 0; i < array.length; i++) {
            if (abs) {
                array[i] = "file://" + urls[i].getPath();
                //array[i] = urls[i].getPath();
            }
            else {
                array[i] = urls[i].getPath();
            }
        }
        return array;
    }

    protected void loadSystemMeta(String xml)
    {
        // allow expansion in both xml attributes and element text
        Substitutor substitutor = new VariableSubstitutor(new JadeVariableExpander());
        Digester digester = new Digester();
        digester.setSubstitutor(substitutor);
        try {
            digester.addRule("system/config/property", new PropertyRule());

            digester.push(SystemDefinition.getInstance());
            //digester.addObjectCreate("system", SystemDefinition.class);
            digester.addObjectCreate("system/servers/server", ServerDefinition.class);
            digester.addSetProperties("system/servers/server");
            digester.addObjectCreate("system/servers/server/config", ConfigDefinition.class);
            digester.addSetProperties("system/servers/server/config");
            digester.addSetNext("system/servers/server/config", "addConfig");
            digester.addSetNext("system/servers/server", "addServer");
            digester.addObjectCreate("system/hosts/host", HostDefinition.class);
            digester.addSetProperties("system/hosts/host");
            digester.addCallMethod("system/hosts/host/server", "addServer", 1);
            digester.addCallParam("system/hosts/host/server", 0, "type");
            digester.addSetNext("system/hosts/host", "addHost");
            digester.setValidating(false);
            digester.parse(new File(xml));
        }
        catch (Throwable t) {
            log.error("Can't parse xml " + xml, t);
        }
    }

    protected class JadeVariableExpander implements VariableExpander
    {
        public String expand(String s)
        {
            return VariableUtils.evaluate(s, null);
        }
    }

    protected class PropertyRule extends Rule
    {
        @Override
        public void begin(String namespace, String name, Attributes attributes)
                throws Exception
        {
            String key = attributes.getValue("name");
            String value = attributes.getValue("value");
            JadeSystem.setProperty(key, replaceValue(value));
        }
    }

    public String replaceValue(String value)
    {
        if (StringUtils.isBlank(value)) {
            return value;
        }

        String real = value;
        if (value.startsWith("{") && value.endsWith("}")) {
            String key = value.substring(1, value.length() - 1);
            real = JadeSystem.getProperty(key, value);
        }
        return real;
    }

}
